# -*- coding: utf-8 -*-
"""
run_system.py

嵌入式代码智能生成系统 - 主控脚本

负责编排系统各个模块，实现从需求分析到代码生成与应用的端到端流程。
"""

import os
import shutil
import json
import configparser
from datetime import datetime

from core.utils import logger, setup_logger, read_file_content, write_json_file
from core.需求分析 import analyze_requirements
#from core.代码预处理 import preprocess_codebase, vectorize_code_fragments
#from core.rag_retrieval import RagRetriever
#from core.代码生成 import CodeGenerator
#from core.代码应用 import CodeApplier, GitManager

# --- 全局配置 --- 
CONFIG_FILE = "config.ini"
DEFAULT_LOG_FILE_DIR = "data/logs"

def load_config():
    """加载配置文件 (config.ini)"""
    config = configparser.ConfigParser()
    if not os.path.exists(CONFIG_FILE):
        logger.warning(f"配置文件 {CONFIG_FILE} 未找到，将使用默认值或尝试创建。")
        create_default_config()
    
    try:
        config.read(CONFIG_FILE, encoding="utf-8")
    except Exception as e:
        logger.error(f"读取配置文件 {CONFIG_FILE} 失败: {e}. 将使用硬编码的默认值。")
        # 如果读取失败，提供一些硬编码的默认值以保证基本运行
        config["Paths"] = {
            "old_project_code_dir": "data/input/old_project_code",
            "new_requirement_file": "data/input/new_requirements.md",
            "old_requirement_file": "data/input/old_requirements.md",
            "disassembly_doc_file": "data/input/disassembly_doc.md",
            "output_dir": "data/output",
            "prompt_template_file": "templates/prompt_template_code_generation.md"
        }
        config["LLM"] = {
            "api_key": "YOUR_CLAUDE_API_KEY_HERE",
            "model_name": "claude-3-opus-20240229"
        }
        config["Git"] = {
            "auto_commit": "True",
            "user_name": "AutoGen System",
            "user_email": "autogen@example.com"
        }
        config["General"] = {
            "log_level": "INFO"
        }

    # 确保关键路径存在，如果配置中没有则使用默认值
    if "Paths" not in config: config["Paths"] = {}
    config["Paths"]["old_project_code_dir"] = config.get("Paths", "old_project_code_dir", fallback="data/input/old_project_code")
    config["Paths"]["new_requirement_file"] = config.get("Paths", "new_requirement_file", fallback="data/input/new_requirements.md")
    config["Paths"]["old_requirement_file"] = config.get("Paths", "old_requirement_file", fallback="data/input/old_requirements.md")
    config["Paths"]["disassembly_doc_file"] = config.get("Paths", "disassembly_doc_file", fallback="data/input/disassembly_doc.md")
    config["Paths"]["output_dir"] = config.get("Paths", "output_dir", fallback="data/output")
    config["Paths"]["prompt_template_file"] = config.get("Paths", "prompt_template_file", fallback="templates/prompt_template_code_generation.md")

    if "LLM" not in config: config["LLM"] = {}
    config["LLM"]["api_key"] = config.get("LLM", "api_key", fallback="YOUR_CLAUDE_API_KEY_HERE")
    config["LLM"]["model_name"] = config.get("LLM", "model_name", fallback="claude-3-opus-20240229")

    if "Git" not in config: config["Git"] = {}
    # 获取布尔值但是以字符串形式保存
    auto_commit_value = config.getboolean("Git", "auto_commit", fallback=True)
    config["Git"]["auto_commit"] = str(auto_commit_value)
    config["Git"]["user_name"] = config.get("Git", "user_name", fallback="AutoGen System")
    config["Git"]["user_email"] = config.get("Git", "user_email", fallback="autogen@example.com")
    
    if "General" not in config: config["General"] = {}
    config["General"]["log_level"] = config.get("General", "log_level", fallback="INFO").upper()

    return config

def create_default_config():
    """创建默认的config.ini文件"""
    config = configparser.ConfigParser()
    config["Paths"] = {
        "old_project_code_dir": "data/input/old_project_code",
        "new_requirement_file": "data/input/new_requirements.md",
        "old_requirement_file": "data/input/old_requirements.md",
        "disassembly_doc_file": "data/input/disassembly_doc.md",
        "output_dir": "data/output",
        "prompt_template_file": "templates/prompt_template_code_generation.md"
    }
    config["LLM"] = {
        "api_key": "YOUR_CLAUDE_API_KEY_HERE", # 请用户替换为自己的API Key
        "model_name": "claude-3-opus-20240229" # 或用户指定的模型
    }
    config["Git"] = {
        "auto_commit": "True",
        "user_name": "AutoGen System",
        "user_email": "autogen@example.com"
    }
    config["General"] = {
        "log_level": "INFO" # 可选: DEBUG, INFO, WARNING, ERROR
    }
    try:
        with open(CONFIG_FILE, "w", encoding="utf-8") as configfile:
            config.write(configfile)
        logger.info(f"已创建默认配置文件: {CONFIG_FILE}。请根据需要修改，特别是LLM API密钥。")
    except Exception as e:
        logger.error(f"创建默认配置文件 {CONFIG_FILE} 失败: {e}")

def setup_workspace(config):
    """准备工作区目录，并将旧代码复制到工作区"""
    output_dir = config.get("Paths", "output_dir")
    old_project_code_dir_orig = config.get("Paths", "old_project_code_dir")

    # 1. 创建输出目录结构
    # 工作区根目录，用于存放本次运行的所有中间和最终产物
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    workspace_root = os.path.join(output_dir, f"run_{timestamp}")
    os.makedirs(workspace_root, exist_ok=True)
    logger.info(f"本次运行的工作区已创建: {workspace_root}")

    paths = {
        "workspace_root": workspace_root,
        "requirement_diff_file": os.path.join(workspace_root, "requirement_diff.json"),
        "parsed_code_fragments_file": os.path.join(workspace_root, "parsed_code_fragments.json"),
        "faiss_index_dir": os.path.join(workspace_root, "faiss_index"),
        "faiss_index_file": os.path.join(workspace_root, "faiss_index", "codebase.index"),
        "faiss_metadata_file": os.path.join(workspace_root, "faiss_index", "metadata.json"),
        "modified_code_dir": os.path.join(workspace_root, "modified_project_code") # 修改后的代码存放于此
    }
    os.makedirs(paths["faiss_index_dir"], exist_ok=True)
    os.makedirs(paths["modified_code_dir"], exist_ok=True)

    # 2. 复制旧项目代码到工作区的 modified_code_dir，后续修改将在此副本上进行
    if not os.path.isdir(old_project_code_dir_orig):
        logger.error(f"原始旧项目代码目录未找到: {old_project_code_dir_orig}。请在config.ini中正确配置。")
        return None
    
    try:
        logger.info(f"正在从 	'{old_project_code_dir_orig}	' 复制旧项目代码到工作区 	'{paths['modified_code_dir']}	' ...")
        # shutil.copytree 在目标目录已存在时会报错，所以先确保目标是空的或删除重建
        if os.path.exists(paths["modified_code_dir"]):
            shutil.rmtree(paths["modified_code_dir"]) # 清空旧的，确保是干净的副本
        shutil.copytree(old_project_code_dir_orig, paths["modified_code_dir"])
        logger.info("旧项目代码复制完成。")
    except Exception as e:
        logger.error(f"复制旧项目代码失败: {e}")
        return None
        
    return paths

def main():
    """系统主流程控制"""
    # 0. 加载配置并设置日志
    cfg = load_config()
    log_level_str = cfg.get("General", "log_level", fallback="INFO").upper()
    log_levels = {"DEBUG": 10, "INFO": 20, "WARNING": 30, "ERROR": 40}
    log_file = os.path.join(DEFAULT_LOG_FILE_DIR, f"system_run_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
    global logger # 更新全局logger实例
    logger = setup_logger(log_file_path=log_file, level=log_levels.get(log_level_str, 20))
    logger.info("智能代码生成系统启动...")
    logger.info(f"配置文件加载自: {CONFIG_FILE}")
    logger.info(f"日志级别设置为: {log_level_str}")
    logger.info(f"详细日志请查看: {os.path.abspath(log_file)}")

    # 检查必要的输入文件是否存在
    required_input_files = [
        cfg.get("Paths", "old_project_code_dir"),
        cfg.get("Paths", "new_requirement_file"),
        cfg.get("Paths", "old_requirement_file"),
        cfg.get("Paths", "disassembly_doc_file"),
        cfg.get("Paths", "prompt_template_file")
    ]
    for f_path in required_input_files:
        if not os.path.exists(f_path):
            logger.error(f"关键输入文件或目录不存在: {f_path}。请检查config.ini配置和文件路径。系统无法继续。")
            return

    # 1. 设置工作区，复制旧代码
    logger.info("--- 阶段1: 设置工作区 ---")
    workspace_paths = setup_workspace(cfg)
    if not workspace_paths:
        logger.error("工作区设置失败，系统终止。")
        return
    
    # 更新LLM API密钥 (如果模块内部直接使用全局变量)
    # 更好的方式是通过参数传递或让模块自己读配置
    # todo  先把代码生成的给注释掉
    #from core.代码生成 import generator as code_gen_module
    #code_gen_module.CLAUDE_API_KEY = cfg.get("LLM", "api_key")
    #code_gen_module.CLAUDE_MODEL_NAME = cfg.get("LLM", "model_name")
    from core.需求分析 import analyzer as req_analyzer_module
    req_analyzer_module.LLM_API_KEY = cfg.get("LLM", "api_key") # 如果需求分析也用LLM

    # 2. 需求分析与差异识别
    logger.info("--- 阶段2: 需求分析与差异识别 ---")
    requirement_diffs = analyze_requirements(
        old_req_file_path=cfg.get("Paths", "old_requirement_file"),
        new_req_file_path=cfg.get("Paths", "new_requirement_file"),
        output_json_path=workspace_paths["requirement_diff_file"]
    )
    if not requirement_diffs:
        logger.warning("需求分析未产生任何差异项，或分析失败。系统将尝试继续，但可能没有代码变更。")
        # return # 如果没有差异，可以选择终止或继续处理空列表
    # 测试用
    return   

    # 3. 旧代码预处理与向量化 (在工作区副本上操作)
    logger.info("--- 阶段3: 旧代码预处理与向量化 ---")
    # 注意：preprocess_codebase 的 code_dir_path 应指向工作区中的代码副本
    parsed_fragments = preprocess_codebase(
        code_dir_path=workspace_paths["modified_code_dir"], # 使用复制到工作区的代码
        output_json_path=workspace_paths["parsed_code_fragments_file"]
    )
    if not parsed_fragments:
        logger.warning("代码预处理未能解析出任何代码片段。后续RAG检索可能受限。")
    
    vectorization_success = vectorize_code_fragments(
        parsed_fragments_file=workspace_paths["parsed_code_fragments_file"],
        faiss_index_path=workspace_paths["faiss_index_file"],
        metadata_path=workspace_paths["faiss_metadata_file"]
    )
    if not vectorization_success:
        logger.warning("代码向量化失败。后续RAG代码检索可能无法进行。")

    # 4. 初始化RAG检索器、代码生成器、代码应用器和Git管理器
    logger.info("--- 阶段4: 初始化核心处理模块 ---")
    rag_retriever = RagRetriever(
        faiss_index_path=workspace_paths["faiss_index_file"],
        faiss_metadata_path=workspace_paths["faiss_metadata_file"],
        disassembly_doc_path=cfg.get("Paths", "disassembly_doc_file"),
        all_parsed_code_fragments_path=workspace_paths["parsed_code_fragments_file"]
    )
    code_generator = CodeGenerator(prompt_template_path=cfg.get("Paths", "prompt_template_file"))
    code_applier = CodeApplier(target_code_base_dir=workspace_paths["modified_code_dir"]) # 修改工作区中的代码
    
    git_manager = None
    if cfg.getboolean("Git", "auto_commit"):
        git_manager = GitManager(repo_path=workspace_paths["modified_code_dir"])
        if not git_manager.is_git_repo():
            logger.info(f"目标代码目录 {workspace_paths['modified_code_dir']} 不是Git仓库，正在初始化..." )
            if git_manager.init_repo():
                git_manager.set_user_config(cfg.get("Git", "user_name"), cfg.get("Git", "user_email"))
                # 初始提交，包含原始代码
                if git_manager.add_files(["."]):
                    git_manager.commit_changes("初始提交: 项目原始代码")
            else:
                logger.error("Git仓库初始化失败，自动提交功能将不可用。")
                git_manager = None # 禁用Git功能
        else:
             logger.info(f"目标代码目录 {workspace_paths['modified_code_dir']} 已是Git仓库。")
             git_manager.set_user_config(cfg.get("Git", "user_name"), cfg.get("Git", "user_email")) # 确保用户信息正确

    # 5. 针对每个需求差异项，进行RAG检索、代码生成、代码应用和Git提交
    logger.info("--- 阶段5: 迭代处理需求变更 ---")
    if not requirement_diffs: # 再次检查，如果之前没有终止
        logger.info("没有识别到需求差异，无需进行代码生成和应用。")
    else:
        for i, req_item in enumerate(requirement_diffs):
            feature_name = req_item.get("feature_name", f"需求变更_{i+1}")
            logger.info(f"\n>>> 开始处理需求: 	'{feature_name}' ({req_item.get('type')}) <<<")

            # 5.1 RAG检索
            logger.info(f"[需求: {feature_name}] 进行RAG检索...")
            rag_context = rag_retriever.retrieve_context(req_item)
            # 可以将rag_context保存到文件供调试
            write_json_file(os.path.join(workspace_paths["workspace_root"], f"rag_context_{feature_name.replace(' ', '_')}.json"), rag_context)

            # 5.2 代码生成建议
            logger.info(f"[需求: {feature_name}] 调用LLM生成代码建议...")
            code_suggestions = code_generator.generate_code_suggestions(req_item, rag_context)
            # 可以将code_suggestions保存到文件供调试
            write_json_file(os.path.join(workspace_paths["workspace_root"], f"code_suggestions_{feature_name.replace(' ', '_')}.json"), code_suggestions)

            if not code_suggestions:
                logger.warning(f"[需求: {feature_name}] 未能生成任何代码建议。跳过应用和提交。")
                continue

            # 5.3 代码应用
            logger.info(f"[需求: {feature_name}] 应用代码变更...")
            s_ops, f_ops = code_applier.apply_changes(code_suggestions)
            logger.info(f"[需求: {feature_name}] 代码应用完成。成功: {s_ops}, 失败: {f_ops}")

            # 5.4 Git提交 (如果启用且有成功应用的操作)
            if git_manager and s_ops > 0:
                logger.info(f"[需求: {feature_name}] 进行Git提交...")
                commit_msg = f"应用需求变更: {feature_name} ({req_item.get('type')})" 
                # 描述可以更详细，例如从 req_item.get('new_description') 提取
                # 确保只添加实际被修改的文件，或者直接 add .
                if git_manager.add_files(["."]): # 添加所有更改
                    if not git_manager.commit_changes(commit_msg):
                        logger.error(f"[需求: {feature_name}] Git提交失败。")
                else:
                    logger.error(f"[需求: {feature_name}] Git添加文件失败。")
            elif git_manager and s_ops == 0 and f_ops == 0 and any(op.get("type") == "general_suggestion" for op in code_suggestions):
                logger.info(f"[需求: {feature_name}] 收到通用建议，无文件变更，不执行Git提交。")
            elif git_manager:
                 logger.info(f"[需求: {feature_name}] 没有成功的代码应用操作，不执行Git提交。")

    logger.info("--- 系统所有流程处理完毕 ---")
    logger.info(f"所有输出和日志保存在工作区: {os.path.abspath(workspace_paths['workspace_root'])}")
    logger.info(f"修改后的代码位于: {os.path.abspath(workspace_paths['modified_code_dir'])}")
    logger.info(f"详细系统运行日志: {os.path.abspath(log_file)}")
    print(f"\n系统运行完成。请查看工作区目录: {os.path.abspath(workspace_paths['workspace_root'])}")
    print(f"修改后的代码项目位于: {os.path.abspath(workspace_paths['modified_code_dir'])}")
    print(f"详细日志文件: {os.path.abspath(log_file)}")

if __name__ == "__main__":
    # 确保脚本从项目根目录运行，以便相对路径正确
    # 或者在脚本开始处动态调整当前工作目录
    # project_root = os.path.dirname(os.path.abspath(__file__))
    # os.chdir(project_root) # 如果run_system.py不在项目根目录
    main()


